/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#pragma once

#include "OmenConfig.h"
#include "OmenDataTypes.h"

#include <string>

namespace Omen
{
    class OMEN_API Matrix3x3
    {
    public:
        Matrix3x3();
        Matrix3x3( const Omen::Float& m11, const Omen::Float& m12, const Omen::Float& m13,
                   const Omen::Float& m21, const Omen::Float& m22, const Omen::Float& m23,
                   const Omen::Float& m31, const Omen::Float& m32, const Omen::Float& m33);

        Matrix3x3( const Matrix3x3& other );

        virtual ~Matrix3x3();
        

        virtual Omen::Float determinant() const;
        virtual Matrix3x3&  inverted( bool* isInvertible = 0 );

        virtual bool        isIdentity() const;
        virtual void        setToIdentity ();
        Matrix3x3           transposed () const;
        
        bool                 operator!= ( const Matrix3x3& other ) const;
        const Omen::Float&   operator() ( int row, int column ) const;
        
        Omen::Float &       operator() ( int row, int column );
        Matrix3x3 &         operator*= ( Omen::Float scalar );
        Matrix3x3 &         operator+= ( const Matrix3x3 & other );
        Matrix3x3 &         operator-= ( const Matrix3x3 & other );
        Matrix3x3 &         operator/= ( Omen::Float scalar );
        bool                operator== ( const Matrix3x3 & other ) const;

    private:
        Omen::Float        m[9]; // The matrix data
    };

    class OMEN_API Matrix4x4 : public OMEN_MATRIX4
    {
    public:
        struct Matrix4x4Proxy
        {
            const Matrix4x4 *mObj;
            int mI;

            OMEN_API Matrix4x4Proxy(const Matrix4x4 *obj, int i)
            : mObj(obj), mI(i)
            {
            }


            OMEN_API Omen::Float operator[](int j) const
            {
                return mObj->m[mI*4+j];
            }

            OMEN_API Omen::Float& operator[](int j)
            {
                return const_cast<Matrix4x4*>(mObj)->m[mI*4+j];
            }
        };

    public:
        Matrix4x4();
        Matrix4x4( const Omen::Float& m11, const Omen::Float& m12, const Omen::Float& m13, const Omen::Float& m14,
                   const Omen::Float& m21, const Omen::Float& m22, const Omen::Float& m23, const Omen::Float& m24,
                   const Omen::Float& m31, const Omen::Float& m32, const Omen::Float& m33, const Omen::Float& m34,
                   const Omen::Float& m41, const Omen::Float& m42, const Omen::Float& m43, const Omen::Float& m44 );

        Matrix4x4( const Matrix4x4& other );

        virtual ~Matrix4x4();

        virtual Omen::Float determinant() const;

        virtual Matrix4x4&  inverted( bool* isInvertible = 0 );
        virtual bool        isIdentity() const;

        virtual void        lookAt( const Omen::Vector3d& eye, const Omen::Vector3d& target, const Omen::Vector3d& up );
        virtual void        setYawPitchRoll( Omen::Float yaw, Omen::Float pitch, Omen::Float roll );    // Creates a rotation matrix from Euler angles
        virtual Matrix3x3   normalMatrix() const;

        virtual float*      float_data() { return m; }

        virtual Omen::Vector3d row( unsigned int row ) const;        /// <summary> Returns the vector of given row </summary>
        virtual Omen::Vector3d column( unsigned int column ) const;    /// <summary> Returns the vector of given column </summary>

        virtual void setRow( unsigned int row, const Omen::Vector3d& v) ;  /// <summary> Returns the vector of given row </summary>
        virtual void setColumn( unsigned int column, const Omen::Vector3d& v ) ;    /// <summary> Returns the vector of given column </summary>

        void     translate ( const Omen::Vector3d& vector );
        void     translate ( Omen::Float x, Omen::Float y );
        void     translate ( Omen::Float x, Omen::Float y, Omen::Float z );

        void     rotateY( Omen::Float radAngle );
        void     rotateX( Omen::Float radAngle );
        void     rotateZ( Omen::Float radAngle );
        
        Matrix4x4     transposed () const;
        
        bool     operator!= ( const Matrix4x4& other ) const;
        
        const Omen::Float&      operator() ( int row, int column ) const;
        Omen::Float&            operator() ( int row, int column );

        Matrix4x4Proxy         operator[] ( int index ) const;
        Matrix4x4Proxy&        operator[] ( int index );

        Matrix4x4&              operator=( const Matrix4x4& other );

        Matrix4x4&              operator*= ( const Matrix4x4 & other );
        Matrix4x4&              operator*= ( Omen::Float scalar );
        Matrix4x4&              operator+= ( const Matrix4x4 & other );
        Matrix4x4&              operator-= ( const Matrix4x4 & other );
        Matrix4x4&              operator/= ( Omen::Float scalar );
        
        Vector3d                operator*( const Omen::Vector3d& v );
        Omen::Matrix4x4         operator*( const Omen::Matrix4x4& m );
        
        bool                    operator== ( const Matrix4x4 & other ) const;

                                operator float*();
    };
} // namespace Omen


OMEN_API Omen::Vector3d operator*( const Omen::Matrix4x4& m, const Omen::Vector3d& v );
OMEN_API Omen::Matrix4x4 operator*( const Omen::Matrix4x4& m1, const Omen::Matrix4x4& m2 );
OMEN_API Omen::Matrix4x4 operator*( Omen::Matrix4x4& m1, Omen::Matrix4x4& m2 );
OMEN_API void printMatrix( const Omen::Matrix4x4& m, std::wstring& wstr );